#ifndef COMMON_HPP
#define COMMON_HPP

#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <mpi.h>
#include <omp.h>
#include "cuda_common.hpp"

// 外迭代精度
#ifndef KSPBIT
#define KSPBIT 64
#endif

// 预条件精度
#ifndef PCBIT
#define PCBIT 64
#endif

#if KSPBIT == 64
typedef double ksp_t;
#elif KSPBIT == 32
typedef float ksp_t;
#endif

#if PCBIT == 64
typedef double pc_t;
#elif PCBIT == 32
typedef float pc_t;
#endif

#define MIN(a, b) ((a) > (b)) ? (b) : (a)
#define MAX(a, b) ((a) > (b)) ? (a) : (b)

/// @brief  读取二进制文件
/// @param buf 目标缓冲区
/// @param filename 文件名 
/// @param size_of_elems 每个元素的大小/字节
/// @param start 起始位置
/// @param nums 读取的元素个数
/// @return 实际读取的元素个数
int read_binary(void * buf, const char * filename, int size_of_elems, long long start, int nums);

/// @brief 检查输入是否合法
/// @param input 输入数组
/// @param num 输入数组的长度
void check_input(const double * input, int num);

/// @brief 键值对的冒泡排序
/// @tparam k_t key的数据类型
/// @tparam v_t value的数据类型
/// @param num 元素个数
/// @param key_arr key数组
/// @param val_arr value数组
template<typename k_t, typename v_t>
void bubble_sort(const int num, k_t * key_arr, v_t * val_arr = nullptr)
{
    if (val_arr == nullptr) {
        for (int k0 = 0; k0 < num - 1; k0++) {
            for (int k1 = 0; k1 < num - 1 - k0; k1++) {
                if (key_arr[k1] > key_arr[k1 + 1]) {
                    k_t tmp_k = key_arr[k1];
                    key_arr[k1] = key_arr[k1 + 1];
                    key_arr[k1 + 1] = tmp_k;
                }// swap
            }// k0 loop
        }// k1 loop
    }
    else {
        for (int k0 = 0; k0 < num - 1; k0++) {
            for (int k1 = 0; k1 < num - 1 - k0; k1++) {
                if (key_arr[k1] > key_arr[k1 + 1]) {
                    k_t tmp_k = key_arr[k1];
                    key_arr[k1] = key_arr[k1 + 1];
                    key_arr[k1 + 1] = tmp_k;
                    v_t tmp_v = val_arr[k1];
                    val_arr[k1] = val_arr[k1 + 1];
                    val_arr[k1 + 1] = tmp_v;
                }// swap
            }// k0 loop
        }// k1 loop
    }
}

/// @brief 用于qsort的比较函数
/// @tparam T 数据类型
/// @param a 指针a
/// @param b 指针b
/// @return 比较结果
template<typename T>
int cmpfunc (const void * a, const void * b)
{
    return ( *(T*)a > *(T*)b ) ? 1 : -1;
}

/// @brief 键值对的去重
/// @tparam k_t key的数据类型
/// @tparam v_t value的数据类型
/// @param num 元素个数
/// @param key_arr key数组
/// @param val_arr value数组
/// @return 去重后的元素个数
template<typename k_t, typename v_t>
int uniquify(const int num, k_t * key_arr, v_t * val_arr = nullptr)
{
    int k0 = 0;// 当前处理到哪个位置的元素
    int uniq_cnt = 0;// 去重后的非零元数
    if (val_arr == nullptr) {
        while (k0 < num) {
            int k1 = k0 + 1;
            while (k1 < num && key_arr[k1] == key_arr[k0]) {
                k1 ++;
            }// 跳出循环时要么k1==num，要么遇到了一个列序号不同的点
            key_arr[uniq_cnt] = key_arr[k0];
            uniq_cnt ++;
            k0 = k1;// 移动指针
        } 
    }
    else {
        while (k0 < num) {
            int k1 = k0 + 1;
            while (k1 < num && key_arr[k1] == key_arr[k0]) {
                val_arr[k0] += val_arr[k1];
                k1 ++;
            }// 跳出循环时要么k1==num，要么遇到了一个列序号不同的点
            key_arr[uniq_cnt] = key_arr[k0];
            val_arr[uniq_cnt] = val_arr[k0];
            uniq_cnt ++;
            k0 = k1;// 移动指针
        }
    }
    assert(k0 == num);// 跳出循环时k0==num
    return uniq_cnt;
}

/// @brief int 原子加法操作
/// @param addr 数据地址
/// @param incr 增量
void atomic_add_int(int * addr, int incr);

/// @brief 线程负载均衡
/// @param tot_work 总工作量
/// @param num_threads 线程数
/// @param tid 线程id
/// @param beg 起始位置，返回值
/// @param end 结束位置，返回值
void thread_load_balance(int tot_work, int num_threads, int tid, int & beg, int & end);

/// @brief 同步：GPU使用cudaDeviceSynchronize，CPU使用MPI_Barrier
void my_barrier();

#endif // UTILS_HPP